/*
 * ConsoleClient - Neko client for Space Creeps that runs in a terminal
 *                 and connects to the database to send audited data
 *
 * Copyright 2008 James W. Hofmann
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

import rules.RulesAPI;

class ConsoleClient

{

	public static function main()
	{
		if (neko.Sys.args().length==0)
		{
			rules.RulesAPI.startgame(null, true);
			neko.Lib.println(string_repr_wave());
			gameloop();
		}
		else
		{
			var recording : Recording = 
				haxe.Unserializer.run(neko.Sys.args()[0]);
			rules.RulesAPI.record = recording.record;
			var recordingcopy = haxe.Unserializer.run(neko.Sys.args()[0]);
			rules.RulesAPI.startgame(recording.seed, false);
			while (recording.record.length>0)
			{
				var nextmsg = recording.record.pop();
				try
				{
				rules.RulesAPI.message([nextmsg]);
				}
				catch (d: Dynamic) {} // ignore bad data
			}
			trace(RulesAPI.g.wavenum);
			trace(RulesAPI.g.framessurvived);
			if (neko.Sys.args()[1]=="store")
			{
				connectandadd(recordingcopy);				
			}
		}
	}

	public static function connectandadd(recording : Recording)
	{
 		
 		CreepData.loadDbInfo();
 
        // initialize the connection
        var useMysql = true;
		var cnx : neko.db.Connection;

		trace("connecting to "+CreepData.databasename+"@"+
		CreepData.databaselocale);

        if( !useMysql )
            cnx = neko.db.Sqlite.open("mydatabase_file.db");
        else {
            cnx = neko.db.Mysql.connect({ 
                host : CreepData.databaselocale,
                port : 3306,
                database : CreepData.databasename,
                user : CreepData.databaselogin,
                pass : CreepData.databasepass,
                socket : null
            });
        }
 
        neko.db.Manager.cnx = cnx;
        neko.db.Manager.initialize();		
 
		CreepData.addScore(recording, 
			RulesAPI.g.framessurvived,
			RulesAPI.g.wavenum);       
			
		neko.Lib.println("Added recording!");
	}
	
	public static function gameloop()
	{
		while (true)
		{
		neko.Lib.println("Energy: "+RulesAPI.g.energy);
		neko.Lib.println("Enter a one-letter command followed by any"+
		" additional parameters(without parens)");
		neko.Lib.println("R)un (# of frames)  W)here is (x,y)  "+ 
		"Place T)ower (x,y)  Place power L)ine (x,y)  Q)uit");
		neko.Lib.print("Command: ");
		var stdin = neko.io.File.stdin();
		var cmd = stdin.readLine();
		var msg = parseinput(cmd);
		if (msg!=null)
			try
			{var responses = RulesAPI.message([msg]);
			 for (r in responses)
			 	parseresponse(r);
			}
			catch (z : rules.GameResponse)
			{
			 neko.Lib.println(z.message);
			 }
			catch (z : Dynamic)
			{
			 neko.Lib.println("****"+Std.string(z));	
			}
		}
	}

	public static function parseresponse(r : rules.GameResponse)
	{
		switch(r.style)
		{
			default:
				try {neko.Lib.println(r.message);} catch(x : Dynamic){}
			case(rules.GameResponse.msg_energychange):
				//neko.Lib.println("energy changed by "+r.message);

			case(rules.GameResponse.msg_fortadd):
				neko.Lib.println("added fort piece");
			case(rules.GameResponse.msg_fortdamage):
				neko.Lib.println("fort takes damage");
			case(rules.GameResponse.msg_fortrepair):
				neko.Lib.println("fort is repaired");
			case(rules.GameResponse.msg_fortkill):
				neko.Lib.println("fort was killed");
			
			case(rules.GameResponse.msg_creepadd):
				neko.Lib.println("creep spawned");
			case(rules.GameResponse.msg_creephurt):
				neko.Lib.println("creep hurt");
			case(rules.GameResponse.msg_creepkill):
				neko.Lib.println("creep killed");
			case(rules.GameResponse.msg_creepmove):
				neko.Lib.println("creep moves");

		}
	}

	public static function parseinput(inp : String) : rules.GameMessage
	{
		var result : rules.GameMessage = null;
		
		var basechar = inp.charAt(0);
		basechar = basechar.toUpperCase();
		
		switch(basechar)
		{
			default:
				neko.Lib.println("Huh?");	
			case("R"): // run
				var numframes : Int;
				try
				{
					numframes = Std.parseInt(inp.substr(1));
					result = new rules.GameMessage(
						rules.GameMessage.msg_runsim,
						numframes);
				}
				catch (x: Dynamic) 
				{neko.Lib.println("Run: Huh?");}
			case("W"): // whereis
				var inter = inp.substr(1);
				var args = inter.split(",");
				try
				{
					var xval = Std.parseInt(args[0]);
					var yval = Std.parseInt(args[1]);
					neko.Lib.println(string_repr_all(xval, yval));
				}	
				catch (x : Dynamic)
				{neko.Lib.println("Where: Huh?");}			
			case("T"): // tower place
				var inter = inp.substr(1);
				var args = inter.split(",");
				try
				{
					var xval = Std.parseInt(args[0]);
					var yval = Std.parseInt(args[1]);
					result = new rules.GameMessage(
							rules.GameMessage.msg_placetower,
							[xval,yval,false]);
				}
				catch (x : Dynamic)
				{neko.Lib.println("Tower: Huh?");}			
			case("L"): // line place
				var inter = inp.substr(1);
				var args = inter.split(",");
				try
				{
					var xval = Std.parseInt(args[0]);
					var yval = Std.parseInt(args[1]);
					result = new rules.GameMessage(
							rules.GameMessage.msg_placeline,
							[xval,yval,false]);
				}
				catch (x : Dynamic)
				{neko.Lib.println("Line: Huh?");}			
			case("Q"): // quit
				var rec = new Recording();
				rec.assign(rules.RulesAPI.record,"Console Player",
							"Console Group", rules.RulesAPI.g.seed);
				neko.Lib.println(haxe.Serializer.run(rec));
				neko.Sys.exit(0);
		}
		return result;
	}

	public static function string_repr_all(?curx : Int, ?cury : Int) 
	: String
	{
		// this is the main string repr function
		// while in a graphical client the data would be held in
		// various graphical containers and updated through messaging,
		// here we simply draw from the game structures each time
		// (performance is less of a concern)
		
		var docheck : Bool = (curx!=null && cury!=null && 
		rules.GameMap.inbounds(curx, cury) );
		
		var tiles = RulesAPI.g.m.tiles;
		
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				var tstr : String;
				if (docheck && x==curx && y==cury)
					tstr = "<>";
				else
					tstr = "  ";
				var tval : String;
				
				var cur = tiles[x][y];
				
				switch(cur.terrain)
				{
					default:
						tval = "  ";
					case(0):
						tval = "__";
					case(1):
						tval = "..";
					case(2):
						tval = "##";
				}
				
				if (cur.fort!=null)
				{
					switch(cur.fort.style)
					{
					case(rules.FortressData.fort_line):
						tval = tval.charAt(0) + "=";
					case(rules.FortressData.fort_base):
						tval = tval.charAt(0) + "B";
					case(rules.FortressData.fort_tower):
						tval = tval.charAt(0) + "T";
					}
				}

				if (cur.creeps.length>0)
				{
					if (cur.creeps.length>9)
						tval = "*" + tval.charAt(1);
					else
						tval = Std.string(cur.creeps.length) +
								tval.charAt(1);
				}
				
				tstr = tstr.charAt(0)+
				tval+
				tstr.charAt(1);
				out+=tstr;
				}
			out+="\n";			
		}
		return out;		
		
	}
	
	// the below are meant for debugging
	//////////////////////////////////////////

    public static function string_repr_terrain() : String
	{
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				out+=Std.string(RulesAPI.g.m.tiles[x][y].terrain);
				if (RulesAPI.g.m.tiles[x][y].terrain<100)
					out+=" ";
				if (RulesAPI.g.m.tiles[x][y].terrain<10)
					out+=" ";
			}
			out+="\n";			
		}
		return out;
	}

    public static function string_repr_wave() : String
	{
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				if 
				(Lambda.exists(RulesAPI.g.wavehash.get(1).path, 
					function(i : lib3f.IntPoint) 
					{return (i.x == x && i.y == y);} 
				))
					out+=" *";
				else out+="  ";		
				out+=Std.string(RulesAPI.g.m.tiles[x][y].terrain);
				if (RulesAPI.g.m.tiles[x][y].terrain<10)
					out+=" ";
			}
			out+="\n";			
		}
		return out;
	}

    public static function string_repr_shore() : String
	{
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				if 
				(Lambda.exists(RulesAPI.g.shorelineinner, 
					function(i : lib3f.IntPoint) 
					{return (i.x == x && i.y == y);} 
				))
					out+=" ~";
				else if 
				(Lambda.exists(RulesAPI.g.shorelineouter, 
					function(i : lib3f.IntPoint) 
					{return (i.x == x && i.y == y);} 
				))
					out+=" !";
				else out+="  ";		
				out+=Std.string(RulesAPI.g.m.tiles[x][y].terrain);
				if (RulesAPI.g.m.tiles[x][y].terrain<10)
					out+=" ";
			}
			out+="\n";			
		}
		return out;
	}

    public static function string_repr_scratchval() : String
	{
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				out+=Std.string(RulesAPI.g.m.tiles[x][y].scratch.value);
				if (RulesAPI.g.m.tiles[x][y].scratch.value<1000)
					out+=" ";
				if (RulesAPI.g.m.tiles[x][y].scratch.value<100)
					out+=" ";
				if (RulesAPI.g.m.tiles[x][y].scratch.value<10)
					out+=" ";
				if (RulesAPI.g.m.tiles[x][y].scratch.value<0)
					out=out.substr(0,out.length-1);
			}
			out+="\n";			
		}
		return out;
	}
	
    public static function string_repr_pathfromscratch() : String
	{
		var path : Array<rules.Node> = new Array();
		
		var cur : rules.Node = null;
		
		while (cur==null || cur.value<10)
		{
			var xval = Std.int(Math.random()*rules.GameMap.mapmaxref);
			var yval = Std.int(Math.random()*rules.GameMap.mapmayref);
			cur = RulesAPI.g.m.tiles[xval][yval].scratch;
		}

		while (cur!=RulesAPI.g.m.tiles[0][0].scratch)
		{
			path.push(cur);
			cur = cur.directions[0];
		}
		
		var out = "";
		for (x in 0...rules.GameMap.mapxsize)
		{
			if (x/2 == Std.int(x/2))
				out+="  ";
			for (y in 0...rules.GameMap.mapysize)
			{
				if (path.remove(RulesAPI.g.m.tiles[x][y].scratch))
				{
					out+="*  ";
				}
				else
				{
					out+=Std.string(RulesAPI.g.m.tiles[x][y].terrain);
					if (RulesAPI.g.m.tiles[x][y].terrain<100)
						out+=" ";
					if (RulesAPI.g.m.tiles[x][y].terrain<10)
						out+=" ";
				}
			}
			out+="\n";			
		}
		return out;
	}	
	
}
